﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using LitJson;
using System.IO;

/// <summary>
/// 工具
/// 缓存Bone对应的顶点列表
/// 缓存Bone影响的所有顶点的最小 最大偏移
/// 两者提供给运行时捏脸使用
/// 需要对称隐射 比如鼻子左右两边 应该是同步偏移的
/// 这些对称隐射 只需要一个slider控制
/// </summary>
public class SaveBoneTool : MonoBehaviour
{

    [MenuItem("GameObject/捏脸/生成Json", false, 1)]
    public static void saveModelMesh()
    {
        //此方法 生成mesh所有数据
        //并生成bone的原始位置信息 提供给编辑最小 最大偏移
        Object obj = Selection.activeObject;
        if (obj == null)
        {
            Debug.Log("请选中需要生成数据的SkinnedMeshRenderer节点");
            return;
        }
        GameObject bodyObj = obj as GameObject;
        SkinnedMeshRenderer meshRenderer = bodyObj.GetComponent<SkinnedMeshRenderer>();
        Transform parent = getParent(bodyObj.transform);
        if (meshRenderer == null)
        {
            meshRenderer = parent.GetComponentInChildren<SkinnedMeshRenderer>();
        }
        if (meshRenderer == null)
        {
            Debug.Log("选中节点未找到SkinnedMeshRenderer");
            return;
        }
        Mesh mesh = meshRenderer.sharedMesh;
        Mesh bakeMesh = new Mesh();
        meshRenderer.BakeMesh(bakeMesh);
        Vector3[] vertexs = bakeMesh.vertices;
        string modelName = parent.name;
        string meshPath = getBoneMeshPath(modelName);
        ModelMeshInfo info = new ModelMeshInfo();
        //顶点位置
        info.vertexPos = new List<JsonVector3>();
        for (int i = 0; i < vertexs.Length; i++)
        {
            JsonVector3 pos = new JsonVector3();
            pos.x = vertexs[i].x;
            pos.y = vertexs[i].y;
            pos.z = vertexs[i].z;
            info.vertexPos.Add(pos);
        }
        //三角形索引
        info.triangles = meshRenderer.sharedMesh.triangles;
        //法线
        info.normals = new List<JsonVector3>();
        Vector3[] normals = meshRenderer.sharedMesh.normals;
        for (int i = 0; i < normals.Length; i++)
        {
            JsonVector3 pos = new JsonVector3();
            pos.x = normals[i].x;
            pos.y = normals[i].y;
            pos.z = normals[i].z;
            info.normals.Add(pos);
        }
        //uv 
        info.uv = new List<JsonVector2>();
        Vector2[] uv = meshRenderer.sharedMesh.uv;
        for (int i = 0; i < uv.Length; i++)
        {
            JsonVector2 pos = new JsonVector2();
            pos.x = uv[i].x;
            pos.y = uv[i].y;
            info.uv.Add(pos);
        }
        //写入
        string txt = JsonMapper.ToJson(info);
        File.WriteAllText(meshPath, txt);
        //生成骨骼对应的顶点列表-----------------------------------------------------------
        string boneVertexPath = getBoneVertexPath(modelName);
        ModelBonesInfo boneVertex = new ModelBonesInfo();
        boneVertex.boneInfo = new List<BoneInfo>();

        BoneWeight[] boneWeights = mesh.boneWeights;
        Dictionary<int, BoneInfo> bonesPool = new Dictionary<int, BoneInfo>();
        for (int i = 0; i < boneWeights.Length; i++)
        {
            //权重大于0的顶点被影响了
            BoneWeight bw = boneWeights[i];
            if (bw.weight0 > 0)
            {
                saveBoneInfo(bw.boneIndex0, i, bw.weight0, bonesPool);
            }
            if (bw.weight0 > 0)
            {
                saveBoneInfo(bw.boneIndex1, i, bw.weight1, bonesPool);
            }
            if (bw.weight0 > 0)
            {
                saveBoneInfo(bw.boneIndex2, i, bw.weight2, bonesPool);
            }
            if (bw.weight0 > 0)
            {
                saveBoneInfo(bw.boneIndex3, i, bw.weight3, bonesPool);
            }
        }
        boneVertex.boneInfo.AddRange(bonesPool.Values);
        string boneVertexTxt = JsonMapper.ToJson(boneVertex);
        File.WriteAllText(boneVertexPath, boneVertexTxt);
        //生成骨骼原始位置------------------------------------------------------------------
        string bonePath = getBoneOffsetPath(modelName);
        Transform[] bones = meshRenderer.bones;
        Dictionary<string, int> boneNameToIndex = new Dictionary<string, int>();
        ModelBonesOffset boneOffset = new ModelBonesOffset();
        boneOffset.bones = new List<BonePos>();
        for (int i = 0; i < bones.Length; i++)
        {
            int bIndex = i;
            Transform boneTrans = bones[i];
            boneNameToIndex.Add(boneTrans.name, bIndex);
            BonePos bonePos = new BonePos();
            if (!bonesPool.ContainsKey(bIndex))
            {
                Debug.LogError("bIndex " + bIndex);
                continue;
            }
            BoneInfo boneInfo = bonesPool[bIndex];
            bonePos.vertexOffsets = new List<BoneVertexOffset>();
            bonePos.boneIndex = bIndex;
            bonePos.bonePos = new JsonVector3();
            bonePos.bonePos.x = boneTrans.localPosition.x;
            bonePos.bonePos.y = boneTrans.localPosition.y;
            bonePos.bonePos.z = boneTrans.localPosition.z;
            for (int m = 0; m < boneInfo.vertexIndexs.Count; m++)
            {
                int vIndex = boneInfo.vertexIndexs[m];
                BoneVertexOffset boneVertexOffset = new BoneVertexOffset();
                boneVertexOffset.vertexIndex = vIndex;
                boneVertexOffset.org = new JsonVector3();
                boneVertexOffset.org.x = vertexs[vIndex].x;
                boneVertexOffset.org.y = vertexs[vIndex].y;
                boneVertexOffset.org.z = vertexs[vIndex].z;

                boneVertexOffset.dtMin = new JsonVector3();
                boneVertexOffset.dtMax = new JsonVector3();
                bonePos.vertexOffsets.Add(boneVertexOffset);
            }
            boneOffset.bones.Add(bonePos);
        }
        //写入
        string boneOffsetTxt = JsonMapper.ToJson(boneOffset);
        File.WriteAllText(bonePath, boneOffsetTxt);
        //生成隐射关系-----------------------------------------------------------------------
        //遍历所有骨骼 l开头 则找r开头的Bone 找到了写入隐射 boneNameToIndex
        string boneOffsetMapPath = getBoneOffsetMapPath(modelName);
        ModelBoneOffsetMap offsetMap = new ModelBoneOffsetMap();
        offsetMap.mainKey = new List<int>();
        offsetMap.subKey = new List<int>();
        for (int i = 0; i < bones.Length; i++)
        {
            string bName = bones[i].name;
            int bIndexLeft = i;
            if (bName.StartsWith("l"))
            {
                string bRName = bName.Substring(1, bName.Length - 1);
                bRName = "r" + bRName;
                if (boneNameToIndex.ContainsKey(bRName))
                {
                    int bIndexRight = boneNameToIndex[bRName];
                    offsetMap.mainKey.Add(bIndexLeft);
                    offsetMap.subKey.Add(bIndexRight);
                }
            }
        }
        string boneOffsetMapTxt = JsonMapper.ToJson(offsetMap);
        File.WriteAllText(boneOffsetMapPath, boneOffsetMapTxt);

        EditorUtility.DisplayDialog("生成捏脸原始数据完成", "生成捏脸原始数据完成", "OK");
    }

    static void saveBoneInfo(int boneIndex, int vertexIndex, float weight, Dictionary<int, BoneInfo> bonesPool)
    {
        if (!bonesPool.ContainsKey(boneIndex))
        {
            bonesPool.Add(boneIndex, new BoneInfo());
            bonesPool[boneIndex].vertexIndexs = new List<int>();
            bonesPool[boneIndex].vertexWeights = new List<double>();
        }
        BoneInfo info = bonesPool[boneIndex];
        if (!info.vertexIndexs.Contains(vertexIndex)) {
            info.boneIndex = boneIndex;
            info.vertexIndexs.Add(vertexIndex);
            info.vertexWeights.Add(weight);
        }   
    }

    [MenuItem("GameObject/捏脸/缓存最小", false, 1)]
    public static void saveMinPos()
    {
        Object obj = Selection.activeObject;
        GameObject bone = obj as GameObject;
        SkinnedMeshRenderer meshRenderer;
        Transform parent = getParent(bone.transform);
        meshRenderer = parent.GetComponentInChildren<SkinnedMeshRenderer>();
        Mesh mesh = meshRenderer.sharedMesh;
        Mesh bakeMesh = new Mesh();
        meshRenderer.BakeMesh(bakeMesh);
        Transform[] bones = meshRenderer.bones;
        string modelName = parent.name;
        int toIndex = -1;
        Vector3 boneOffset = Vector3.zero;
        ModelBonesOffset modelBonesOffset = null;
        BonePos bonePos = null;
        string path = "";
        int boneIndex = -1;
        Vector3[] vertexs = null;
        ModelBonesInfo boneInfo = null;
        for (int i = 0; i < bones.Length; i++)
        {
            if (bones[i].name == bone.name)
            {
                boneIndex = i;
                //offset json
                string boneStr = File.ReadAllText(getBoneVertexPath(modelName));
                boneInfo = JsonMapper.ToObject<ModelBonesInfo>(boneStr);
                //offsetMap json
                string boneoffsetMapStr = File.ReadAllText(getBoneOffsetMapPath(modelName));
                ModelBoneOffsetMap boneOffsetMapInfo = JsonMapper.ToObject<ModelBoneOffsetMap>(boneoffsetMapStr);
                for (int k = 0; k < boneOffsetMapInfo.mainKey.Count; k++)
                {
                    if (boneOffsetMapInfo.mainKey[k] == boneIndex)
                    {
                        toIndex = boneOffsetMapInfo.subKey[k];
                        break;
                    }
                    if (boneOffsetMapInfo.subKey[k] == boneIndex)
                    {
                        toIndex = boneOffsetMapInfo.mainKey[k];
                        break;
                    }
                }
                //读取缓存
                path = getBoneOffsetPath(modelName);
                string json = File.ReadAllText(path);
                modelBonesOffset = JsonMapper.ToObject<ModelBonesOffset>(json);
                //信息
                vertexs = bakeMesh.vertices;
                //通过boneIndex获取找到offset                
                for (int m = 0; m < modelBonesOffset.bones.Count; m++)
                {
                    if (modelBonesOffset.bones[m].boneIndex == boneIndex)
                    {
                        bonePos = modelBonesOffset.bones[m];
                        break;
                    }
                }
                JsonVector3 jv3 = bonePos.bonePos;
                Vector3 boneOrgPos = new Vector3((float)jv3.x, (float)jv3.y, (float)jv3.z);
                boneOffset = (boneOrgPos - bones[i].localPosition);
                //通过boneIndex获取找到所有影响的顶点
                BoneInfo bInfo = null;
                for (int m = 0; m < boneInfo.boneInfo.Count; m++)
                {
                    if (boneInfo.boneInfo[m].boneIndex == boneIndex)
                    {
                        bInfo = boneInfo.boneInfo[m];
                        break;
                    }
                }
                for (int m = 0; m < bInfo.vertexIndexs.Count; m++)
                {
                    int vIndex = bInfo.vertexIndexs[m];
                    BoneVertexOffset boneVertexOffset = null;
                    for (int k = 0; k < bonePos.vertexOffsets.Count; k++)
                    {
                        if (bonePos.vertexOffsets[k].vertexIndex == vIndex)
                        {
                            boneVertexOffset = bonePos.vertexOffsets[k];
                        }
                    }
                    Vector3 vPos = vertexs[vIndex];//编辑出的最小位置
                    JsonVector3 oPos = boneVertexOffset.org;
                    boneVertexOffset.dtMin.x = vPos.x - oPos.x;
                    boneVertexOffset.dtMin.y = vPos.y - oPos.y;
                    boneVertexOffset.dtMin.z = vPos.z - oPos.z;
                }
                break;
            }
        }

        //写入
        if (modelBonesOffset != null)
        {
            if (toIndex > 0)
            {
                saveSymIndexMin(toIndex, boneOffset, bones, meshRenderer, boneInfo, modelBonesOffset);
            }
            string txt = JsonMapper.ToJson(modelBonesOffset);
            File.WriteAllText(path, txt);
            Debug.Log("缓存成功 选中index " + boneIndex + "  对称index " + toIndex);
        }
        else
        {
            Debug.Log("缓存失败  没有找到骨骼 真实骨骼index " + boneIndex);
        }
    }

    [MenuItem("GameObject/捏脸/缓存最大", false, 2)]
    public static void saveMaxPos()
    {
        Object obj = Selection.activeObject;
        GameObject bone = obj as GameObject;
        SkinnedMeshRenderer meshRenderer;
        Transform parent = getParent(bone.transform);
        meshRenderer = parent.GetComponentInChildren<SkinnedMeshRenderer>();
        Mesh mesh = meshRenderer.sharedMesh;
        Mesh bakeMesh = new Mesh();
        meshRenderer.BakeMesh(bakeMesh);
        Transform[] bones = meshRenderer.bones;
        string modelName = parent.name;
        int toIndex = -1;
        Vector3 boneOffset = Vector3.zero;
        ModelBonesOffset modelBonesOffset = null;
        BonePos bonePos = null;
        string path = "";
        int boneIndex = -1;
        Vector3[] vertexs = null;
        ModelBonesInfo boneInfo = null;
        for (int i = 0; i < bones.Length; i++)
        {
            if (bones[i].name == bone.name)
            {
                boneIndex = i;
                //offset json
                string boneStr = File.ReadAllText(getBoneVertexPath(modelName));
                boneInfo = JsonMapper.ToObject<ModelBonesInfo>(boneStr);
                //offsetMap json
                string boneoffsetMapStr = File.ReadAllText(getBoneOffsetMapPath(modelName));
                ModelBoneOffsetMap boneOffsetMapInfo = JsonMapper.ToObject<ModelBoneOffsetMap>(boneoffsetMapStr);
                for (int k = 0; k < boneOffsetMapInfo.mainKey.Count; k++)
                {
                    if (boneOffsetMapInfo.mainKey[k] == boneIndex)
                    {
                        toIndex = boneOffsetMapInfo.subKey[k];
                        break;
                    }
                    if (boneOffsetMapInfo.subKey[k] == boneIndex)
                    {
                        toIndex = boneOffsetMapInfo.mainKey[k];
                        break;
                    }
                }
                //读取缓存
                path = getBoneOffsetPath(modelName);
                string json = File.ReadAllText(path);
                modelBonesOffset = JsonMapper.ToObject<ModelBonesOffset>(json);
                //信息
                vertexs = bakeMesh.vertices;
                //通过boneIndex获取找到offset                
                for (int m = 0; m < modelBonesOffset.bones.Count; m++)
                {
                    if (modelBonesOffset.bones[m].boneIndex == boneIndex)
                    {
                        bonePos = modelBonesOffset.bones[m];
                        break;
                    }
                }
                JsonVector3 jv3 = bonePos.bonePos;
                Vector3 boneOrgPos = new Vector3((float)jv3.x, (float)jv3.y, (float)jv3.z);
                boneOffset = (boneOrgPos - bones[i].localPosition);
                //通过boneIndex获取找到所有影响的顶点
                BoneInfo bInfo = null;
                for (int m = 0; m < boneInfo.boneInfo.Count; m++)
                {
                    if (boneInfo.boneInfo[m].boneIndex == boneIndex)
                    {
                        bInfo = boneInfo.boneInfo[m];
                        break;
                    }
                }
                for (int m = 0; m < bInfo.vertexIndexs.Count; m++)
                {
                    int vIndex = bInfo.vertexIndexs[m];
                    BoneVertexOffset boneVertexOffset = null;
                    for (int k = 0; k < bonePos.vertexOffsets.Count; k++)
                    {
                        if (bonePos.vertexOffsets[k].vertexIndex == vIndex)
                        {
                            boneVertexOffset = bonePos.vertexOffsets[k];
                        }
                    }
                    Vector3 vPos = vertexs[vIndex];//编辑出的最大位置
                    JsonVector3 oPos = boneVertexOffset.org;
                    boneVertexOffset.dtMax.x = vPos.x - oPos.x;
                    boneVertexOffset.dtMax.y = vPos.y - oPos.y;
                    boneVertexOffset.dtMax.z = vPos.z - oPos.z;
                }
                break;
            }
        }

        //写入
        if (modelBonesOffset != null)
        {
            if (toIndex > 0)
            {
                saveSymIndexMax(toIndex, boneOffset, bones, meshRenderer, boneInfo, modelBonesOffset);
            }
            string txt = JsonMapper.ToJson(modelBonesOffset);
            File.WriteAllText(path, txt);
            Debug.Log("缓存成功 选中index " + boneIndex + "  对称index " + toIndex);
        }
        else
        {
            Debug.Log("缓存失败  没有找到骨骼 真实骨骼index " + boneIndex);
        }
    }

    //缓存对称index 最小  saveSymIndexMin
    private static void saveSymIndexMin(int boneIndex, Vector3 offset, Transform[] bones, SkinnedMeshRenderer skinnedMeshRenderer, ModelBonesInfo boneInfo, ModelBonesOffset modelBonesOffset)
    {
        offset.y *= -1;
        //找到骨骼
        for (int i = 0; i < bones.Length; i++)
        {
            if (i == boneIndex)
            {
                BonePos bonePos = null;
                //通过boneIndex获取找到offset                
                for (int m = 0; m < modelBonesOffset.bones.Count; m++)
                {
                    if (modelBonesOffset.bones[m].boneIndex == boneIndex)
                    {
                        bonePos = modelBonesOffset.bones[m];
                        break;
                    }
                }
                JsonVector3 jv3 = bonePos.bonePos;
                Vector3 boneOrgPos = new Vector3((float)jv3.x, (float)jv3.y, (float)jv3.z);
                bones[i].localPosition = boneOrgPos + offset;
                //通过boneIndex获取找到所有影响的顶点
                BoneInfo bInfo = null;
                for (int m = 0; m < boneInfo.boneInfo.Count; m++)
                {
                    if (boneInfo.boneInfo[m].boneIndex == boneIndex)
                    {
                        bInfo = boneInfo.boneInfo[m];
                        break;
                    }
                }
                for (int m = 0; m < bInfo.vertexIndexs.Count; m++)
                {
                    int vIndex = bInfo.vertexIndexs[m];
                    BoneVertexOffset boneVertexOffset = null;
                    for (int k = 0; k < bonePos.vertexOffsets.Count; k++)
                    {
                        if (bonePos.vertexOffsets[k].vertexIndex == vIndex)
                        {
                            boneVertexOffset = bonePos.vertexOffsets[k];
                        }
                    }
                    Mesh bakeMesh = new Mesh();
                    skinnedMeshRenderer.BakeMesh(bakeMesh);
                    Vector3[] vertexs = bakeMesh.vertices;
                    Vector3 vPos = vertexs[vIndex];//编辑出的最小位置
                    JsonVector3 oPos = boneVertexOffset.org;
                    boneVertexOffset.dtMin.x = vPos.x - oPos.x;
                    boneVertexOffset.dtMin.y = vPos.y - oPos.y;
                    boneVertexOffset.dtMin.z = vPos.z - oPos.z;
                }
                break;
            }
        }
    }
    //saveSymIndexMax   
    private static void saveSymIndexMax(int boneIndex, Vector3 offset, Transform[] bones, SkinnedMeshRenderer skinnedMeshRenderer, ModelBonesInfo boneInfo, ModelBonesOffset modelBonesOffset)
    {
        offset.y *= -1;
        //找到骨骼
        for (int i = 0; i < bones.Length; i++)
        {
            if (i == boneIndex)
            {
                BonePos bonePos = null;
                //通过boneIndex获取找到offset                
                for (int m = 0; m < modelBonesOffset.bones.Count; m++)
                {
                    if (modelBonesOffset.bones[m].boneIndex == boneIndex)
                    {
                        bonePos = modelBonesOffset.bones[m];
                        break;
                    }
                }
                JsonVector3 jv3 = bonePos.bonePos;
                Vector3 boneOrgPos = new Vector3((float)jv3.x, (float)jv3.y, (float)jv3.z);
                bones[i].localPosition = boneOrgPos + offset;
                //通过boneIndex获取找到所有影响的顶点
                BoneInfo bInfo = null;
                for (int m = 0; m < boneInfo.boneInfo.Count; m++)
                {
                    if (boneInfo.boneInfo[m].boneIndex == boneIndex)
                    {
                        bInfo = boneInfo.boneInfo[m];
                        break;
                    }
                }
                for (int m = 0; m < bInfo.vertexIndexs.Count; m++)
                {
                    int vIndex = bInfo.vertexIndexs[m];
                    BoneVertexOffset boneVertexOffset = null;
                    for (int k = 0; k < bonePos.vertexOffsets.Count; k++)
                    {
                        if (bonePos.vertexOffsets[k].vertexIndex == vIndex)
                        {
                            boneVertexOffset = bonePos.vertexOffsets[k];
                        }
                    }
                    Mesh bakeMesh = new Mesh();
                    skinnedMeshRenderer.BakeMesh(bakeMesh);
                    Vector3[] vertexs = bakeMesh.vertices;
                    Vector3 vPos = vertexs[vIndex];//编辑出的最大位置
                    JsonVector3 oPos = boneVertexOffset.org;
                    boneVertexOffset.dtMax.x = vPos.x - oPos.x;
                    boneVertexOffset.dtMax.y = vPos.y - oPos.y;
                    boneVertexOffset.dtMax.z = vPos.z - oPos.z;
                }
                break;
            }
        }
    }

    /// <summary>
    /// 偏移 path
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    private static string getBoneOffsetPath(string name)
    {
        string subName = name + "_BoneOffset";
        string path = Path.Combine(Application.dataPath, "JsonInfo/" + subName + ".txt");
        checkPath(path);
        return path;
    }
    /// <summary>
    /// Bone权重顶点 path
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    private static string getBoneVertexPath(string name)
    {
        string subName = name + "_BoneVertex";
        string path = Path.Combine(Application.dataPath, "JsonInfo/" + subName + ".txt");
        checkPath(path);
        return path;
    }
    /// <summary>
    /// mesh顶点
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    private static string getBoneMeshPath(string name)
    {
        string subName = name + "_BoneMesh";
        string path = Path.Combine(Application.dataPath, "JsonInfo/" + subName + ".txt");
        checkPath(path);
        return path;
    }

    private static string getBoneOffsetMapPath(string name)
    {
        string subName = name + "_BoneOffsetMap";
        string path = Path.Combine(Application.dataPath, "JsonInfo/" + subName + ".txt");
        checkPath(path);
        return path;
    }

    private static void checkPath(string path)
    {
        if (!File.Exists(path))
        {
            FileStream fileStream = File.Create(path);
            fileStream.Close();
        }
    }

    private static Transform getParent(Transform trans)
    {
        if (trans.parent == null)
        {
            return trans;
        }
        else
        {
            return getParent(trans.parent);
        }
    }
}
